/*************************************************************************
	> File Name: server.c
	> Author: 
	> Mail: 
	> Created Time: 2015年01月13日 星期二 16时11分47秒
 ************************************************************************/

#include<pthread.h>
#include<math.h>
#include"errors.h"

#define CLIENT_THREAD   4

#define REQ_READ        1 // read with prompt
#define REQ_WRITE       2 // write
#define REQ_QUIT        3 //quit server

typedef struct request_tag{
    struct request_tag *next;           // link to next
    int                 operation;      //Function code
    int                 synchronous;    //Nonzero if synchronous
    int                 done_flag;      //predicate for wait
    pthread_cond_t      done;           //wait for completion
    char                prompt[32];      //prompt string for reads
    char                text[128];      //read/write text
} request_t;

typedef struct tty_server_tag{
    request_t           *first;
    request_t           *last;
    int                 running;
    pthread_mutex_t     mutex;
    pthread_cond_t      request;
} tty_server_t;

//static context for server

tty_server_t tty_server = {
    NULL,NULL,0,
    PTHREAD_MUTEX_INITIALIZER,PTHREAD_COND_INITIALIZER
};

int client_thread;
pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  client_done = PTHREAD_COND_INITIALIZER; 


void *tty_server_routine(void* arg){
    static pthread_mutex_t prompt_mutex = PTHREAD_MUTEX_INITIALIZER;
    request_t * request;
    int operation,len;
    int status;

    while(1){
        if((status = pthread_mutex_lock(&tty_server.mutex)) ){
            err_abort(status,"lock server mutex");
        }
        //printf("server routine got server mutex\n");
        while(tty_server.first == NULL){
            //printf("server routine wait for server request\n");
            status = pthread_cond_wait(&tty_server.request,&tty_server.mutex);
            if(status) err_abort(status,"wait for request");
        }
        request = tty_server.first;
        tty_server.first = request->next;

        //printf("server routine got request\n");

        if(tty_server.first == NULL){
            tty_server.last = NULL;
        }

         status = pthread_mutex_unlock(&tty_server.mutex);
         if(status) err_abort(status,"unlock server mutex");
        //printf("server routine unlock the server mutex\n");

        

        // process to data
        operation = request->operation;
        switch(operation){
            case REQ_QUIT:
            break;
            case REQ_READ:
                if(strlen(request->prompt) > 0) printf("%s\n",request->prompt);
                if(fgets(request->text,128,stdin) == NULL) request->text[0] = '\0';

                // turn the null into '\0'
                len = strlen(request->text);
                if(len > 0 && request->text[len - 1] == '\n') request->text[len - 1] = '\0';
            break;
            case REQ_WRITE:
                puts(request->text);
            break;
            default:
            break;
        }


        if(request->synchronous){
            status = pthread_mutex_lock(&tty_server.mutex);
            if(status) err_abort(status,"lock server mutex");
            
            request->done_flag = 1;

            status = pthread_cond_signal(&request->done);
            if(status) err_abort(status,"signal request done");

            //printf("signal to client request done\n");

            status = pthread_mutex_unlock(&tty_server.mutex);
            if(status) err_abort(status,"unlock server mutex");
        }else{
            free(request);
        }
        if(operation == REQ_QUIT) break;
    }
    return NULL;
}

void tty_server_request(int operation,int sync,const char *prompt,char *string,int thread_id){
    request_t * request;
    int status;
/*
    //printf("server request %d: op :%d sync :%d prompt :%s text: %s\n",thread_id,
          operation,sync,prompt,string);
*/

    status = pthread_mutex_lock(&tty_server.mutex);
    if(status) err_abort(status,"lock server mutex");
    //printf("server_request %d  got mutex\n",thread_id);

    if(!tty_server.running){
        pthread_t thread;
        pthread_attr_t detached_attr;

        status = pthread_attr_init(&detached_attr);
        if(status) err_abort(status,"Init attributes object");

        status = pthread_attr_setdetachstate(&detached_attr,PTHREAD_CREATE_DETACHED);
        if(status) err_abort(status,"Set detach state");

        tty_server.running = 1;

        status = pthread_create(&thread,&detached_attr,tty_server_routine,NULL);
        if(status) err_abort(status,"create server");
        //printf(" %d create tty_server_thread \n",thread_id);

        pthread_attr_destroy(&detached_attr);
    }
    request = (request_t*) malloc(sizeof(request_t));
    if(NULL == request) errno_abort("Allocate request");
    request->next = NULL;
    request->operation = operation;
    request->synchronous = sync;
    if(sync){
        request->done_flag = 0;
        status = pthread_cond_init(&request->done,NULL);
        if(status) err_abort(status,"Init request condition");
        //printf("pthread_cond_init  %d request done\n",thread_id);
    }
    if(prompt != NULL){
        strncpy(request->prompt,prompt,32); 
    }else{
        request->prompt[0] = '\0';
    }
    if(operation == REQ_WRITE && string != NULL){
        strncpy(request->text,string,128);
    }else{
        request->text[0] = '\0';
    }


    // add the request to the queue,maintaining the first and 
    // the last pointer
    if(tty_server.first == NULL){
        tty_server.first = tty_server.last = request;
    }else{
        (tty_server.last)->next = request;
        tty_server.last = request;
    }

    // tell server the request is available
    status = pthread_cond_signal(&tty_server.request);
    if(status) err_abort(status,"wake server");
    //printf(" %d signal to tty_server request\n",thread_id);

    if(sync){
        while(!request->done_flag){
            //printf(" server_request  %d wait for request done\n",thread_id);
            status = pthread_cond_wait(&request->done,&tty_server.mutex);
            if(status) err_abort(status, "wait for sync request");

            //printf(" %d receiver a signal for request done\n",thread_id);
            if(operation == REQ_READ){
                if(strlen(request->text) > 0){
                    strcpy(string,request->text);
                }else{
                    string[0] = '\0';
                }
            }
            status = pthread_cond_destroy(&request->done);
            if(status) err_abort(status,"destroy request condition");
            free(request);
        }
    }

    status = pthread_mutex_unlock(&tty_server.mutex);
    if(status) err_abort(status,"unlock mutex");
}

void * client_routine(void* arg){
    int my_number = (int) arg, loops;
    char prompt [32];
    char string [128];
    char formatted [128];
    int status;

    sprintf(prompt,"Client %d > ",my_number);
    while(1){
        tty_server_request(REQ_READ,1,prompt,string,my_number);
        if(strlen(string) == 0) break;
        
        for(loops = 0;loops < 4;loops++){
            sprintf(formatted, "(client thread id %d # loops %d) %s\n",my_number,loops,string);
            tty_server_request(REQ_WRITE,0,NULL,formatted,my_number);
            sleep(1);
        }
    }
    status = pthread_mutex_lock(&client_mutex);
    if(status) err_abort(status,"Lock client mutex");
    //printf("client thread %d locked client muitex\n",my_number);
    client_thread--;
    if(client_thread <= 0){
        status = pthread_cond_signal(&client_done);
        if(status) err_abort(status,"Signal client done");
        //printf("client routine signal to client done\n");
    }
    status = pthread_mutex_unlock(&client_mutex);
    if(status) err_abort(status,"Unlock client mutex");
    //printf("client thread %d  unlocked client muitex\n",my_number);
     
    return NULL;
}


int main(int argc ,char *argv[]){
    pthread_t thread;
    int count;
    int status;


    // create client threads
    client_thread = CLIENT_THREAD;
    for(count = 0; count < client_thread;count++){
        status = pthread_create(&thread,NULL,client_routine,(void*)count);
        if(status) err_abort(status,"create client thread");
        //printf("create thread %d\n",count);
    }

    status = pthread_mutex_lock(&client_mutex);
    if(status) err_abort(status,"lock client mutex");

    while(client_thread > 0){
        status = pthread_cond_wait(&client_done,&client_mutex);
        if(status) err_abort(status,"Wait for client to finish");
    }

    status = pthread_mutex_unlock(&client_mutex);
    if(status) err_abort(status,"Unlock client mutex");

    //printf("All client done\n");
    tty_server_request(REQ_QUIT,1,NULL,NULL,-1);
    return 0;
}
